package com.thingple.usb.stream;

import android.os.ParcelFileDescriptor;
import android.util.Log;

import com.thingple.usb.listener.SessionTraceListener;
import com.thingple.usb.listener.StreamDownListener;
import com.thingple.usb.listener.StreamUpListener;
import com.thingple.usb.manager.UsbErrorListener;
import com.thingple.util.Strings;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

public abstract class AbstractProcessor {
    private static final String TAG = AbstractProcessor.class.getName();

    FileInputStream is;
    FileOutputStream os;
    final Object inputLock = new Object();
    final Object outputLock = new Object();
    ByteBuffer inputBuffer;
    ByteBuffer outputBuffer;

    ParcelFileDescriptor pfd;

    //////////////

    boolean work;
    int readSize;

    SessionTraceListener traceListener;
    StreamUpListener streamUpListener;
    StreamDownListener streamDownListener;
    UsbErrorListener errorListener;

    public void setTraceListener(SessionTraceListener listener) {
        traceListener = listener;
    }
    public void setStreamUpListener(StreamUpListener listener) {
        streamUpListener = listener;
    }
    public void setStreamDownListener(StreamDownListener listener) {
        streamDownListener = listener;
    }
    public void setErrorListener(UsbErrorListener listener) {
        errorListener = listener;
    }

    /**
     * 立刻发送
     * @param payload bytes
     */
    public void write(byte[] payload) {
        if (payload == null || payload.length == 0) {
            return;
        }
        Log.d(TAG, "tx:" + Strings.hex(payload));
        if (os == null) {
            onErr(new Exception("输出流未初始化"));
            return;
        }

        try {
            os.write(payload);
            onTx(payload, payload.length, false);
        } catch (IOException e) {
            onErr(e);
        }

    }

    /**
     * 写入输出缓存中
     * @param payload bytes
     */
    public void writeAsync(byte[] payload) {
        if (payload == null || payload.length == 0) {
            return;
        }
        Log.d(TAG, "tx:" + Strings.hex(payload));
        // TODO
        if (outputBuffer == null) {
            return;
        }
        synchronized (outputLock) {
            outputBuffer.put(payload);
            onTx(payload, payload.length, true);
        }

    }


    public void stop() {
        work = false;
    }

    public void loop() {
        work = true;
        onTrace("开始I/O循环");
        int step = 0;
        boolean hasNext = true;
        while(hasNext && work) {
            delay();
            step = (step+1)%2; // 拢共两步
            if (step == 0) {
                hasNext = tx();
            } else {
                hasNext = rx();
            }
        }
        onTrace("结束I/O循环");
    }

    protected void delay() {
        try {
            Thread.sleep(5);
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    protected boolean tx() {
        byte[] buf = null;
        if (outputBuffer == null) { // DO NOTHING
            return true;
        }
        if (os == null) {
            return true;
        }
        synchronized (outputLock) {

            int len = outputBuffer.position();
            if (len > 0) {
                buf = new byte[len];
                outputBuffer.rewind();
                outputBuffer.get(buf, 0, len);
                outputBuffer.clear();
                onTrace("Tx bytes: " + len);
            }
        }
        if (buf != null) {
            try{
                os.write(buf);
            } catch (Exception e) {
                onTrace("Tx异常:" + e);
                onErr(e);
                return false;
            }
        }
        return true;
    }

    protected boolean rx() {
        byte[] buf;
        if (inputBuffer == null) {
            onTrace("inputBuffer null");
            return true;
        }
        if (is == null) {
            onTrace("is null");
            return true;
        }

        synchronized (inputLock) {
            buf = inputBuffer.array();
        }
        try {
            readSize = is.read(buf, 0, 1024);
        } catch (IOException e) {
//            onTrace("Rx异常" + e);
//            onErr(e);
            return false;
        }
        if (readSize > 0) { // 读到新数据
            byte[] payload = new byte[readSize];
            System.arraycopy(buf, 0, payload, 0, readSize);
            onRx(payload, readSize);
        }
        return true;
    }

    protected abstract void onTx(byte[] bytes, int n, boolean async);
    protected abstract void onRx(byte[] bytes, int n);
    protected abstract void onTrace(String content);
    protected abstract void onErr(Exception e);


    public void onDestroy() {

        try { // 关闭输入流
            if (is != null) {
                is.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        is = null;

        try {// 关闭输出流
            if (os != null) {
                os.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        os = null;

        if (inputBuffer != null) { // 释放输入buffer
            inputBuffer.clear();
            inputBuffer = null;
        }
        if (outputBuffer != null) {// 释放输出buffer
            outputBuffer.clear();
            outputBuffer = null;
        }

        if (pfd != null) {
            try {
                pfd.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
